
const fs = require('fs');
const path = require('path');

let genId = function (i) {
    if (i < 10) { return '0000' + i }
    else if (i < 100) { return '000' + i }
    else if (i < 1000) { return '00' + i }
    else if (i < 10000) { return '0' + i }
    else { return i }
}

String.prototype.format = function (args) {
    var result = this;
    if (arguments.length > 0) {
        if (arguments.length == 1 && typeof (args) == "object") {
            for (var key in args) {
                if (args[key] != undefined) {
                    var reg = new RegExp("({" + key + "})", "g");
                    result = result.replace(reg, args[key]);
                }
            }
        }
        else {
            for (var i = 0; i < arguments.length; i++) {
                if (arguments[i] != undefined) {
                    var reg = new RegExp("({)" + i + "(})", "g");
                    result = result.replace(reg, arguments[i]);
                }
            }
        }
    }
    return result;
}
//调用readFile方法，给到文件路径以及成功和失败的值

let readFile = async function (file) {
    // file = path.join(__dirname, file)
    return new Promise((resolve, reject) => {
        fs.readFile(file, function (err, data) {
            //判断读取是否成功，输出想对应的值
            // err == null ? console.log(data.toString()) : console.log("读取失败" + err);
            if (!err) {
                resolve(data.toString())
            } else {
                reject("error")
            }

        })
    });
}

let writeFile = async function (file, data) {
    // file = path.join(__dirname, file)
    return new Promise((resolve, reject) => {
        console.log(file);
        fs.writeFile(file, data, function (err) {
            if (err) {
                reject("写入失败" + err);
            } else {
                resolve("写入成功");
            }
        })
    });
}

class GenProject {

    constructor(world_name, width, height, points, robots, dataURL) {
        this.world_name = world_name
        this.width = width
        this.height = height
        this.points = points
        this.robots = robots
        this.dataURL = dataURL
    }

    async writeWorld() {
        let wf = `
<sdf version="1.7">
  <world name="world">
  <scene>
      <ambient>0.8 0.8 0.8 1.0</ambient>
      <background>0 0 0</background>
      <grid>false</grid>
  </scene>

  <include>
      <uri>model://sun</uri>
  </include>
  
  <include>
      <name>{world_name}</name>
      <uri>model://{world_name}</uri>
      <pose>0 0 0.0 0 0 0</pose>
  </include>

  {includes}
  
  </world>
</sdf>`
        wf = wf.replace(/{world_name}/g, this.world_name)
        let fname = './views/world/{0}.world'.format(this.world_name);

        let statics = []
        let robots = this.robots
        let incluceStr = ""
        for (let i = 0; i < robots.length; i++) {
            const robot = robots[i];
            if (robot.props.static) { //add static models
                let strToAdd = ` 
              <include>
                  <name>{robot_id}</name>
                  <uri>model://{robot_name}</uri>
                  <pose>{x} {y} {z} {roll} {pitch} {yaw}</pose>
              </include>`
                strToAdd = strToAdd.replace(/{robot_id}/g, robot.props.name)
                strToAdd = strToAdd.replace(/{robot_name}/g, robot.props.name)
                let x = (robot.xPercent * this.width)
                let y = (robot.yPercent * this.height)
                let z = robot.props.z
                strToAdd = strToAdd.replace(/{x}/g, x)
                strToAdd = strToAdd.replace(/{y}/g, -y)
                strToAdd = strToAdd.replace(/{z}/g, z)
                strToAdd = strToAdd.replace(/{roll}/g, 0)
                strToAdd = strToAdd.replace(/{pitch}/g, 0)
                strToAdd = strToAdd.replace(/{yaw}/g, (robot.angle / 360) * 2 * 3.14)
                incluceStr += strToAdd
            }

        }
        wf = wf.replace(/{includes}/g, incluceStr)
        let res = await writeFile(fname, wf)
        return res
    }
    async writeModelConfig() {
        let wf = `
<?xml version='1.0' encoding='utf-8'?>
<model>
<name>{world_name}</name>
<version>1.0.0</version>
<sdf version="1.6">model.sdf</sdf>
<author>
    <name>automatically generated from the Great Editor</name>
    <email>info@openrobotics.org</email>
</author>
<description>level {world_name} (automatically generated)</description>
</model>

 `
        wf = wf.replace(/{world_name}/g, this.world_name)

        let fname = './views/world/{0}/model.config'.format(this.world_name);
        let res = await writeFile(fname, wf)
        return res
    }

    async writeModelSdf() {
        let wf = `
<?xml version='1.0' encoding='utf-8'?>
<sdf version="1.7">
  <model name="{world_name}">
    <static>true</static>
    <link name="{name}">
      <visual name="visual">
        <geometry>
          <mesh>
            <uri>model://{world_name}/meshes/{name}.obj</uri>
          </mesh>
        </geometry>
        <material>
          <diffuse>1.0 1.0 1.0</diffuse>
          <specular>0.1 0.1 0.1</specular>
          <pbr>
            <metal>
              <metalness>0.0</metalness>
              <albedo_map>model://{world_name}/meshes/{world_name}.png</albedo_map>
            </metal>
          </pbr>
          <script>
            <uri>model://{world_name}/meshes/</uri>
            <name>{name}_Diffuse</name>
          </script>
        </material>
      </visual>
      <collision name="collision">
        <geometry>
          <mesh>
            <uri>model://{world_name}/meshes/{name}.obj</uri>
          </mesh>
        </geometry>
        <surface>
          <contact>
            <collide_bitmask>0x01</collide_bitmask>
          </contact>
        </surface>
      </collision>
    </link>
  </model>

  <model name="marks">
    {mark_png_s}
  </model>
</sdf>
 `

        let points = this.points
        let marksStr = ""
        for (let i = 0; i < points.length; i++) {
            const point = points[i];
            if (point.props.static == false) { //add static models
                let strToAdd = `
       <link name="{linkName}">
             <pose>{x} {y} 0.0001 0 0 0</pose>
             <inertial>
               <mass>0.390</mass>
             </inertial>
             <collision name="collision">
               <geometry>
                       <box>
                         <size>{size} {size} .0001</size>
                       </box>
               </geometry>
             </collision>
             <visual name="visual">
               <geometry>
                       <box>
                         <size>{size} {size} .0001</size>
                       </box>
               </geometry>
               <material>
                 <script>
                   <uri>model://apriltag/materials/scripts</uri>
                   <uri>model://apriltag/materials/textures</uri>
                   <name>Mark{index}/Diffuse </name>
                 </script>
               </material>
             </visual>
           </link>
        `
                strToAdd = strToAdd.replace(/{linkName}/g, i)
                strToAdd = strToAdd.replace(/{size}/g, '0.5')
                strToAdd = strToAdd.replace(/{index}/g, i)
                let x = (point.xPercent * this.width)
                let y = (point.yPercent * this.height)
                let z = point.props.z
                strToAdd = strToAdd.replace(/{x}/g, x)
                strToAdd = strToAdd.replace(/{y}/g, -y)
                marksStr += strToAdd
            }

        }

        wf = wf.replace(/{world_name}/g, this.world_name)
        wf = wf.replace(/{name}/g, this.world_name)
        wf = wf.replace(/{mark_png_s}/g, marksStr)
        let fname = './views/world/{0}/model.sdf'.format(this.world_name);
        let res = await writeFile(fname, wf)
        return res
    }

    async writeTextures() {
        let points = this.points
        let texturesStr = ""
        for (let i = 0; i < points.length; i++) {
            const point = points[i];
            if (point.props.static == false) { //add static models
                let strToAdd = `
material Mark{index}/Diffuse
{
  technique
  {
    pass
    {
      texture_unit
      {
        texture {pngName}
        filtering anistropic
        max_anisotropy 16
      }
    }
  }
}`
                strToAdd = strToAdd.replace(/{index}/g, i)
                let pngName = 'tag49_12_{0}.png'.format(genId(i));
                strToAdd = strToAdd.replace(/{pngName}/g, pngName)
                let x = (point.xPercent * this.width)
                let y = (point.yPercent * this.height)
                let z = point.props.z
                strToAdd = strToAdd.replace(/{x}/g, x)
                strToAdd = strToAdd.replace(/{y}/g, -y)
                texturesStr += strToAdd
            }

        }
        let fname = '../doc/models/apriltag/materials/scripts/marks.material';
        let res = await writeFile(fname, texturesStr)
        return res
    }

    async writeMeshesMtl() {
        let wf = `
# Blender 3.5.0 MTL File: 'None'
# www.blender.org
newmtl {world_name}
Ns 50.000011
Ka 1.000000 1.000000 1.000000
Ks 1.000000 1.000000 1.000000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
map_Kd {world_name}.png

 `
        wf = wf.replace(/{world_name}/g, this.world_name)
        let fname = './views/world/{0}/meshes/{0}.mtl'.format(this.world_name);
        let res = await writeFile(fname, wf)
        return res
    }
    async writeMeshesObj() {
        let wf = `
# Brave 
# brave@5ftech.com
mtllib {name}.mtl
o {name}
v {x1} {y1} 0
v {x1} {y1} -0.1
v {x4} {y4} 0
v {x4} {y4} -0.1
v {x3} {y3} 0
v {x3} {y3} -0.1
v {x2} {y2} 0 0
v {x2} {y2} -0.1
vn -0.0000 -0.0000 1.0000
vn -0.0000 -0.0000 -1.0000
vt 0.000000 1.000000
vt 0.000000 1.000000
vt 0.000000 -0.000000
vt -0.000000 0.000000
vt 1.000000 0.000000
vt 1.000000 -0.000000
vt 1.000000 1.000000
vt 1.000000 1.000000
s 0
usemtl {name}
f 3/3/1 7/7/1 1/1/1
f 2/2/2 8/8/2 4/4/2
f 3/3/1 5/5/1 7/7/1
f 8/8/2 6/6/2 4/4/2
 `
        //顺时针的顺序 按 左上 右上 右下 左下  
        let point1 = { x: 0, y: 0 }
        let point2 = { x: this.width, y: 0 }
        let point3 = { x: this.width, y: parseFloat(this.height) * -1 }
        let point4 = { x: 0, y: parseFloat(this.height) * -1 }
        wf = wf.replace(/{name}/g, this.world_name)
        wf = wf.replace(/{x1}/g, point1.x)
        wf = wf.replace(/{y1}/g, point1.y)
        wf = wf.replace(/{x2}/g, point2.x)
        wf = wf.replace(/{y2}/g, point2.y)
        wf = wf.replace(/{x3}/g, point3.x)
        wf = wf.replace(/{y3}/g, point3.y)
        wf = wf.replace(/{x4}/g, point4.x)
        wf = wf.replace(/{y4}/g, point4.y)
        let fname = './views/world/{0}/meshes/{0}.obj'.format(this.world_name);
        let res = await writeFile(fname, wf)
        return res
    }
    async writeMeshesPng() {
        let data = this.dataURL
        let fname = './views/world/{0}/meshes/{0}.png'.format(this.world_name);
        const base64 = data.replace(/^data:image\/\w+;base64,/, ""); //去掉图片base64码前面部分data:image/png;base64
        // new Buffer 操作权限太大，v6.0后使用Buffer.from()创建构造函数
        const dataBuffer = new Buffer(base64, 'base64'); //把base64码转成buffer对象，

        return new Promise((resolve, reject) => {
            fs.writeFile(path.resolve(fname), dataBuffer, function (err) {//用fs写入文件
                if (err) {
                    reject(err);
                } else {
                    resolve('写入成功！');
                }
            })
        });

    }

    async writeLaunch() {
        let wf = `
      <launch>
    <!-- 设置launch文件的参数 -->
    <arg name="world_name" value="$(find wheeltec_description)/worlds/{world_name}.world"/> <!--地图文件位置及命名-->
    <arg name="paused" default="false"/> <!--以暂停状态打开Gazebo 默认为false-->
    <arg name="use_sim_time" default="true"/> <!--节点启动模拟时间，启动主题topic /clock 默认为true-->
    <arg name="gui" default="true"/> <!--启动Gazebo用户界面接口 默认为true-->
    <arg name="headless" default="false"/><!--Gazebo已弃用，不起任何作用，定义仍保留以防止打破其他launch启动-->
    <arg name="debug" default="false"/><!--用gdb模式启动gzserver（Gazebo 服务器） 默认为false-->
    <!-- 运行gazebo仿真环境 -->
    <include file="$(find gazebo_ros)/launch/empty_world.launch">
        <arg name="world_name" value="$(arg world_name)" />
        <arg name="debug" value="$(arg debug)" />
        <arg name="gui" value="$(arg gui)" />
        <arg name="paused" value="$(arg paused)"/>
        <arg name="use_sim_time" value="$(arg use_sim_time)"/>
        <arg name="headless" value="$(arg headless)"/>
    </include>

   
    {spawn_models}

</launch>
      `
        wf = wf.replace(/{world_name}/g, this.world_name)
        let fname = './views/world/{0}.launch'.format(this.world_name);
        let statics = []
        let robots = this.robots
        let incluceStr = ""
        for (let i = 0; i < robots.length; i++) {
            const robot = robots[i];
            if (robot.props.static == false) { //add static models
                let strToAdd = ` 
              <node pkg="gazebo_ros" type="spawn_model" name="{spawn_urdf_name}" 
              args="-sdf  -file /home/lvxun/.gazebo/models/{type}/model.sdf  -model {spawn_urdf_name}  -x {x} -y {y} -z {z} -R {roll} -P {pitch} -Y {yaw}"/> 
              `
                strToAdd = strToAdd.replace(/{type}/g, robot.props.name)
                strToAdd = strToAdd.replace(/{spawn_urdf_name}/g, robot.props.name + robot.id)
                strToAdd = strToAdd.replace(/{robot_namespace}/g, robot.id)
                let x = (robot.xPercent * this.width)
                let y = (robot.yPercent * this.height)
                let z = robot.props.z
                strToAdd = strToAdd.replace(/{x}/g, x)
                strToAdd = strToAdd.replace(/{y}/g, -y)
                strToAdd = strToAdd.replace(/{z}/g, z)
                strToAdd = strToAdd.replace(/{roll}/g, 0)
                strToAdd = strToAdd.replace(/{pitch}/g, 0)
                strToAdd = strToAdd.replace(/{yaw}/g, (robot.angle / 360) * 2 * 3.14)
                incluceStr += strToAdd
            }

        }
        wf = wf.replace(/{spawn_models}/g, incluceStr)
        let res = await writeFile(fname, wf)
        return res


    }


}

module.exports = GenProject 